package cn.faster.sys.service.impl;

import cn.faster.common.enums.EnumUserOperate;
import cn.faster.common.utils.sys.shiro.ShiroSaltUtils;
import cn.faster.common.event.UserActionEvent;
import cn.faster.config.minio.MinioConfig;
import cn.faster.sys.entity.SysMenu;
import cn.faster.sys.entity.SysRole;
import cn.faster.sys.entity.SysUser;
import cn.faster.sys.entity.SysUserRoleCenter;
import cn.faster.sys.mapper.SysRoleMapper;
import cn.faster.sys.mapper.SysUserMapper;
import cn.faster.sys.mapper.SysUserRoleCenterMapper;
import cn.faster.sys.service.SysMenuService;
import cn.faster.sys.service.SysUserService;
import cn.faster.sys.vo.SysUserVO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.swagger.models.auth.In;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author xiaoCoder
 * @since 2021-06-06
 */
@Slf4j
@Service
@AllArgsConstructor
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {


    private final SysUserMapper sysUserMapper;

    private final SysUserRoleCenterMapper sysUserRoleCenterMapper;

    private final SysRoleMapper sysRoleMapper;

    private final MinioConfig minioConfig;

    private final SysMenuService sysMenuService;

    private final ApplicationEventPublisher applicationEventPublisher;


    @Override
    public List<SysUser> getUserList(SysUser sysUser) {
        return sysUserMapper.selectListByUser(sysUser);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SysUserVO saveRole(SysUserVO user) throws Exception {
        String salt = ShiroSaltUtils.getSalt(4);
        user.setPassword(new Md5Hash(user.getPassword(), salt, 1024).toString());
        user.setSalt(salt);
        // 添加用户
        user.setHeardImg(upload(user.getMultipartFile()).get("name"));
        sysUserMapper.insert(user);
        user.setMultipartFile(null);
        // 增加用户权限
        List<SysRole> sysRoles = sysRoleMapper.selectList(new QueryWrapper<SysRole>().eq("lev", user.getLev()));
        sysRoles.forEach(role -> {
            SysUserRoleCenter sysUserRoleCenter = new SysUserRoleCenter();
            sysUserRoleCenter.setUId(user.getId());
            sysUserRoleCenter.setRId(role.getId());
            sysUserRoleCenterMapper.insert(sysUserRoleCenter);
        });
        return user;
    }

    /**
     * 退出功能
     *
     * @return
     */
    @Override
    public String loginOut() {
        SysUser sysUser = (SysUser) SecurityUtils.getSubject().getPrincipal();
        log.info("用户：{} 退出登录", sysUser.getName());
        SecurityUtils.getSubject().logout();

        UserActionEvent userActionEvent = new UserActionEvent(this);  // 发布：退出的通知信息
        userActionEvent.setSysUser(sysUser);
        userActionEvent.setOperate(EnumUserOperate.LOGOUT);
        userActionEvent.setSuccess(true);

        applicationEventPublisher.publishEvent(userActionEvent);
        log.info("开始发布loginOut通知:{}", userActionEvent);
        return "退出成功！";
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Map<String, String> upload(MultipartFile file) throws Exception {
        return minioConfig.upload(file);
    }

    /**
     * 重写添加用户信息，增加用户图片信息
     *
     * @param userVO
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysUserVO save(SysUserVO userVO) throws Exception {
        // 清空 数据格式信息
        return saveRole(userVO);
    }

    @Override
    public IPage<SysUser> getUserListByPage(SysUser sysUser, Page<SysUser> sysUserPage) {
        return sysUserMapper.selectPage(sysUserPage, null);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysUserVO updateById(SysUserVO userVO) throws Exception {
        SysUser sysUser = sysUserMapper.selectById(userVO.getId());
        String heardImg = sysUser.getHeardImg();
        if (userVO.getMultipartFile().isEmpty()) {
            // 没有更改图片
            userVO.setHeardImg(heardImg);
        } else {
            // 插入新图片，并将图片插入服务器
            userVO.setHeardImg(upload(userVO.getMultipartFile()).get("name"));
            // 删除服务器上面的旧图片
            String removeImg = minioConfig.remove(heardImg);
            log.info("删除minio服务器上面的旧图片:{}", removeImg);
        }
        userVO.setMultipartFile(null);
        sysUserMapper.updateById(userVO);
        return userVO;
    }


    @Override
    public SysUserVO getUserRole(Integer id) {
        SysUserVO sysUserVO = new SysUserVO();
        // 获取菜单路由信息
        List<SysMenu> sysMenus = sysMenuService.getBaseMapper().selectList(null);
        // 找到所有的一级菜单
        List<SysMenu> rootList = new ArrayList<>();
        for (SysMenu menu : sysMenus) {
            if (menu.getParentId() == 0) rootList.add(menu);
        }
        // 为一级菜单设置子菜单
        for (SysMenu menu : sysMenus) {
            menu.setList(getChild(menu.getId(), sysMenus));
        }
        sysUserVO.setSysMenus(rootList);
        return sysUserVO;
    }

    private List<?> getChild(Long id, List<SysMenu> sysMenus) {
        ArrayList<SysMenu> childList = new ArrayList<>();
        for (SysMenu menu : sysMenus) {
            // 遍历所有节点，将父菜单id与传过来的id比较
            if (menu.getParentId().equals(id)) {
                childList.add(menu);
            }
        }

        // 循环一遍子菜单
        for (SysMenu menu : childList) {
            menu.setList(getChild(menu.getId(), sysMenus));
        }
        // 退出条件
        if (childList.size() == 0) {
            return null;
        }
        return childList;
    }
}
